package com.seven.smartsnmp.discover.service;

import com.seven.smartsnmp.config.DiscoverConfig;
import com.seven.smartsnmp.discover.DeviceInfo;
import com.seven.smartsnmp.discover.LinkInfo;
import com.seven.smartsnmp.discover.PortInfo;
import com.sun.jmx.snmp.Timestamp;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author seven
 * @create 2019-06-17 13:35
 **/
public class SearchTopoRelationService {

    DeviceInfoManager deviceManager;
    List<LinkInfo> allLinkList = new ArrayList<>();

    public SearchTopoRelationService() {
    }

    /**
     * 创建连接关系
     *
     * @param deviceInfos
     * @return
     */
    public List<LinkInfo> createAllLinks(CopyOnWriteArrayList<DeviceInfo> deviceInfos) {
        System.out.println("开始发现链接关系......：" + new Timestamp(System.currentTimeMillis()));
        this.deviceManager = new DeviceInfoManager(deviceInfos);
        //获取交换机信息 没有交换机的当成是终端设备
        List switchList = this.deviceManager.switchList;
        List devList = new ArrayList();
        Iterator it = switchList.iterator();
        while (it.hasNext()) {
            DeviceInfo deviceInfo = (DeviceInfo) it.next();
            devList.add(deviceInfo);
            if (DiscoverConfig.isDebug) {
                System.out.println("\n=============================");
                System.out.println("正在分析设备的连接关系：" + deviceInfo);
            }
            List portInfoList = deviceInfo.getPortInfoList();
            Iterator portIt = portInfoList.iterator();
            while (portIt.hasNext()) {
                PortInfo portInfo = (PortInfo) portIt.next();
                this.checkPortLeafDevice(portInfo);
                List list = portInfo.subDeviceList;
                Iterator iterator = list.iterator();
                while (true) {
                    DeviceInfo deviceInfo1 = null;
                    do {
                        if (!iterator.hasNext()) {
                            continue;
                        }
                        deviceInfo1 = (DeviceInfo) iterator.next();
                    } while (devList.contains(deviceInfo1));
                    List portInfoList1 = deviceInfo1.getPortInfoList();
                    Iterator iterator1 = portInfoList1.iterator();
                    while (iterator1.hasNext()) {
                        PortInfo portInfo1 = (PortInfo) iterator1.next();
                        this.checkTwoPortLink(portInfo, portInfo1);
                    }
                }
            }
        }
        System.out.println("=============================");
        System.out.println(new Timestamp(System.currentTimeMillis()) + "链接分析完备，发现的链接个数:" + this.allLinkList.size());
        it = this.allLinkList.iterator();

        while (it.hasNext()) {
            LinkInfo linkInfo = (LinkInfo) it.next();
            System.out.println(linkInfo);
        }

        return this.allLinkList;
    }

    private void addLink(LinkInfo linkInfo) {
        if (!this.allLinkList.contains(linkInfo)) {
            Iterator iterator = this.allLinkList.iterator();
            while (iterator.hasNext()) {
                LinkInfo lInfo = (LinkInfo) iterator.next();
                int lNum = lInfo.checkFuzzyOrAccurate(linkInfo);
                if (lNum == 1) {
                    iterator.remove();
                    if (DiscoverConfig.isDebug) {
                        System.out.println("修正原来的模糊链接:" + lInfo + "修正后:" + linkInfo);
                    }
                } else if (lNum == 0) {
                    if (DiscoverConfig.isDebug) {
                        System.out.println("丢弃模糊链接" + linkInfo);
                    }

                    return;
                }
            }
            if (DiscoverConfig.isDebug) {
                System.out.println("加入新的链接:" + linkInfo);
            }
            this.allLinkList.add(linkInfo);
        }
    }

    /**
     * 查看是否是末端设备
     *
     * @param portInfo
     */
    private void checkPortLeafDevice(PortInfo portInfo) {
        if (portInfo.subDeviceList.size() == 1) {
            DeviceInfo deviceInfo = (DeviceInfo) portInfo.subDeviceList.get(0);
            PortInfo port = new PortInfo();
            port.device = deviceInfo;
            port.port = -1;
            LinkInfo linkInfo = this.creatLink(portInfo, port, true);
            if (DiscoverConfig.isDebug) {
                System.out.println("发现末端链接:" + linkInfo);
            }
            this.addLink(linkInfo);
        } else {
            if (portInfo.portMacList.size() == 1) {
                String mac = (String) portInfo.portMacList.get(0);
                if (this.deviceManager.getDeviceInfoByMAC(mac) == null) {
                    String ip = this.deviceManager.getIPOfMac(mac);
                    if (ip != null) {
                        DeviceInfo dev = new DeviceInfo();
                        dev.setDeviceIP(ip);
                        dev.setDeviceMAC(mac);
                        PortInfo pInfo = new PortInfo();
                        pInfo.device = dev;
                        pInfo.port = -1;
                        LinkInfo linkInfo = this.creatLink(portInfo, pInfo, false);
                        if (DiscoverConfig.isDebug) {
                            System.out.println("发现外部链接:" + linkInfo);
                        }

                        this.addLink(linkInfo);
                    }
                }
            }

        }
    }

    /**
     * 查看端口连接情况
     *
     * @param pOne
     * @param pTwo
     */
    private void checkTwoPortLink(PortInfo pOne, PortInfo pTwo) {
        DeviceInfo dOne = pOne.device;
        List subListOne = pOne.subDeviceList;
        DeviceInfo dTwo = pTwo.device;
        List subListTwo = pTwo.subDeviceList;
        if (subListOne.contains(dTwo) && subListTwo.contains(dOne)) {
            ArrayList list = new ArrayList();
            Iterator oneIt = subListOne.iterator();
            while (oneIt.hasNext()) {
                DeviceInfo deviceInfo = (DeviceInfo) oneIt.next();
                if (subListTwo.contains(deviceInfo)) {
                    list.add(deviceInfo);
                }
            }

            if (list.size() <= 0) {
                LinkInfo linkInfo = this.creatLink(pOne, pTwo, true);
                if (DiscoverConfig.isDebug) {
                    System.out.println("发现精确链接:" + linkInfo);
                }
                this.addLink(linkInfo);
            } else if (list.size() == 1) {
                DeviceInfo deviceInfo = (DeviceInfo) list.get(0);
                PortInfo portInfo = new PortInfo();
                portInfo.device = deviceInfo;
                portInfo.port = -1;
                LinkInfo linkInfoOne = this.creatLink(pOne, portInfo, true);
                LinkInfo linkInfoTwo = this.creatLink(pTwo, portInfo, true);
                if (DiscoverConfig.isDebug) {
                    System.out.println("发现模糊链接:" + linkInfoOne);
                    System.out.println("发现模糊链接:" + linkInfoTwo);
                }
                this.addLink(linkInfoOne);
                this.addLink(linkInfoTwo);
            }
        }

    }

    /**
     * 创建连接关系
     *
     * @param
     * @param
     * @param
     * @return
     */
    private LinkInfo creatLink(PortInfo pOne, PortInfo pTwo, boolean isIn) {
        DeviceInfo devOne = pOne.device;
        int pOneNum = pOne.port;
        DeviceInfo devTwo = pTwo.device;
        int portNum = pTwo.port;
        LinkInfo linkInfo = new LinkInfo();
        if (!isIn) {
            linkInfo.linkType = LinkInfo.LinkTypeOutbound;
        }

        if (devOne.getDeviceIP().compareTo(devTwo.getDeviceIP()) > 0 && isIn) {
            linkInfo.startDevice = devTwo;
            linkInfo.startPortInfo = pTwo;
            linkInfo.endDevice = devOne;
            linkInfo.endPortInfo = pOne;
            linkInfo.startPort = portNum;
            linkInfo.endPort = pOneNum;
        } else {
            linkInfo.startDevice = devOne;
            linkInfo.startPortInfo = pOne;
            linkInfo.endDevice = devTwo;
            linkInfo.endPortInfo = pTwo;
            linkInfo.startPort = pOneNum;
            linkInfo.endPort = portNum;
        }

        return linkInfo;
    }
}
